home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1997 / MacHack 1997.toast / Hacks / Hacks ’96 / VideoFolder 1.0a / Source / MoreFiles 1.4.1 / Search.c < prev    next >
Text File  |  1995-12-21  |  37KB  |  1,224 lines

  1. /*
  2. **    Apple Macintosh Developer Technical Support
  3. **
  4. **    IndexedSearch and the PBCatSearch compatibility function.
  5. **
  6. **    by Jim Luther, Apple Developer Technical Support Emeritus
  7. **
  8. **    File:        Search.c
  9. **
  10. **    Copyright © 1992-1995 Apple Computer, Inc.
  11. **    All rights reserved.
  12. **
  13. **    You may incorporate this sample code into your applications without
  14. **    restriction, though the sample code has been provided "AS IS" and the
  15. **    responsibility for its operation is 100% yours.  However, what you are
  16. **    not permitted to do is to redistribute the source as "DSC Sample Code"
  17. **    after having made changes. If you're going to re-distribute the source,
  18. **    we require that you make it clear in the source that the code was
  19. **    descended from Apple Sample Code, but that you've made changes.
  20. */
  21.  
  22. #include <Types.h>
  23. #include <Gestalt.h>
  24. #include <Timer.h>
  25. #include <Errors.h>
  26. #include <Memory.h>
  27. #include <Files.h>
  28. #include <TextUtils.h>
  29. #include "MoreFiles.h"
  30. #include "MoreFilesExtras.h"
  31. #include "Search.h"
  32.  
  33. /*****************************************************************************/
  34.  
  35. enum
  36. {
  37.     /* Number of LevelRecs to add each time the searchStack is grown */
  38.     /* 20 levels is probably more than reasonable for most volumes. */
  39.     /* If more are needed, they are allocated 20 levels at a time. */
  40.     kAdditionalLevelRecs = 20
  41. };
  42.  
  43. /*****************************************************************************/
  44.  
  45. /*
  46. **    LevelRecs are used to store the directory ID and index whenever
  47. **    IndexedSearch needs to either scan a sub-directory, or return control
  48. **    to the caller because the call has timed out or the number of
  49. **    matches requested has been found. LevelRecs are stored in an array
  50. **    used as a stack.
  51. */
  52. struct    LevelRec
  53. {
  54.     long    dirModDate;    /* for detecting most (but not all) catalog changes */
  55.     long    dirID;
  56.     short    index;
  57. };
  58. typedef struct LevelRec LevelRec;
  59. typedef LevelRec *LevelRecPtr, **LevelRecHandle;
  60.  
  61.  
  62. /*
  63. **    SearchPositionRec is my version of a CatPositionRec. It holds the
  64. **    information I need to resuming searching.
  65. */
  66. #if PRAGMA_ALIGN_SUPPORTED
  67. #pragma options align=mac68k
  68. #endif
  69. struct SearchPositionRec
  70. {
  71.     long            initialize;        /* Goofy checksum of volume information used to make */
  72.                                     /* sure we're resuming a search on the same volume. */
  73.     unsigned short    stackDepth;        /* Current depth on searchStack. */
  74.     short            priv[11];        /* For future use... */
  75. };
  76. #if PRAGMA_ALIGN_SUPPORTED
  77. #pragma options align=reset
  78. #endif
  79. typedef struct SearchPositionRec SearchPositionRec;
  80. typedef SearchPositionRec *SearchPositionRecPtr;
  81.  
  82.  
  83. /*
  84. **    ExtendedTMTask is a TMTask record extended to hold the timer flag.
  85. */
  86. #if PRAGMA_ALIGN_SUPPORTED
  87. #pragma options align=mac68k
  88. #endif
  89. struct ExtendedTMTask
  90. {
  91.     TMTask            theTask;
  92.     Boolean            stopSearch;        /* the Time Mgr task will set stopSearch to */
  93.                                     /* true when the timer expires */
  94. };
  95. #if PRAGMA_ALIGN_SUPPORTED
  96. #pragma options align=reset
  97. #endif
  98. typedef struct ExtendedTMTask ExtendedTMTask;
  99. typedef ExtendedTMTask *ExtendedTMTaskPtr;
  100.  
  101. /*****************************************************************************/
  102.  
  103. static    OSErr    CheckVol(StringPtr pathname,
  104.                          short vRefNum,
  105.                          short *realVRefNum,
  106.                          long *volID);
  107.  
  108. static    OSErr    CheckStack(unsigned short stackDepth,
  109.                            LevelRecHandle searchStack,
  110.                            Size *searchStackSize);
  111.  
  112. static    OSErr    VerifyUserPB(CSParamPtr userPB,
  113.                              Boolean *includeFiles,
  114.                              Boolean *includeDirs,
  115.                              Boolean *includeNames);
  116.  
  117. static    Boolean    IsSubString(StringPtr aStringPtr,
  118.                             StringPtr subStringPtr);
  119.  
  120. static    Boolean    CompareMasked(const long *data1,
  121.                               const long *data2,
  122.                               const long *mask,
  123.                               short longsToCompare);
  124.  
  125. static    void    CheckForMatches(CInfoPBPtr cPB,
  126.                                 CSParamPtr userPB,
  127.                                 const Str63 matchName,
  128.                                 Boolean includeFiles,
  129.                                 Boolean includeDirs);
  130.  
  131. #if GENERATINGCFM
  132.  
  133. static    pascal    void    TimeOutTask(TMTaskPtr tmTaskPtr);
  134.  
  135. #else
  136.  
  137. static    pascal    TMTaskPtr    GetTMTaskPtr(void);
  138.  
  139. static    void    TimeOutTask(void);
  140.  
  141. #endif
  142.  
  143. static    long    GetDirModDate(short vRefNum,
  144.                               long dirID);
  145.  
  146. /*****************************************************************************/
  147.  
  148. /*
  149. **    CheckVol gets the volume's real vRefNum and builds a volID. The volID
  150. **    is used to help insure that calls to resume searching with IndexedSearch
  151. **    are to the same volume as the last call to IndexedSearch.
  152. */
  153. static    OSErr    CheckVol(StringPtr pathname,
  154.                          short vRefNum,
  155.                          short *realVRefNum,
  156.                          long *volID)
  157. {
  158.     HParamBlockRec pb;
  159.     Str255 tempPathname;
  160.     OSErr error;
  161.  
  162.     pb.volumeParam.ioVRefNum = vRefNum;
  163.     if ( pathname == NULL )
  164.     {
  165.         pb.volumeParam.ioNamePtr = NULL;
  166.         pb.volumeParam.ioVolIndex = 0;        /* use ioVRefNum only */
  167.     }
  168.     else
  169.     {
  170.         BlockMoveData(pathname, tempPathname, pathname[0] + 1);    /* make a copy of the string and */
  171.         pb.volumeParam.ioNamePtr = (StringPtr)tempPathname;    /* use the copy so original isn't trashed */
  172.         pb.volumeParam.ioVolIndex = -1;    /* use ioNamePtr/ioVRefNum combination */
  173.     }
  174.     error = PBHGetVInfoSync(&pb);
  175.     if ( error == noErr )
  176.     {
  177.         /* Return the real vRefNum */
  178.         *realVRefNum = pb.volumeParam.ioVRefNum;
  179.  
  180.         /* Add together a bunch of things that aren't supposed to change on */
  181.         /* a mounted volume that's being searched and that should come up with */
  182.         /* a fairly unique number */
  183.         *volID = pb.volumeParam.ioVCrDate +
  184.                  pb.volumeParam.ioVRefNum +
  185.                  pb.volumeParam.ioVNmAlBlks +
  186.                  pb.volumeParam.ioVAlBlkSiz +
  187.                  pb.volumeParam.ioVFSID;
  188.     }
  189.     return ( error );
  190. }
  191.  
  192. /*****************************************************************************/
  193.  
  194. /*
  195. **    CheckStack checks the size of the search stack (array) to see if there's
  196. **    room to push another LevelRec. If not, CheckStack grows the stack by
  197. **    another kAdditionalLevelRecs elements.
  198. */
  199. static    OSErr    CheckStack(unsigned short stackDepth,
  200.                            LevelRecHandle searchStack,
  201.                            Size *searchStackSize)
  202. {
  203.     OSErr    result;
  204.     
  205.     if ( (*searchStackSize / sizeof(LevelRec)) == (stackDepth + 1) )
  206.     {
  207.         /* Time to grow stack */
  208.         SetHandleSize((Handle)searchStack, *searchStackSize + (kAdditionalLevelRecs * sizeof(LevelRec)));
  209.         result = MemError();    /* should be noErr */
  210.         *searchStackSize = GetHandleSize((Handle)searchStack);
  211.     }
  212.     else
  213.     {
  214.         result = noErr;
  215.     }
  216.     
  217.     return ( result );
  218. }
  219.  
  220. /*****************************************************************************/
  221.  
  222. /*
  223. **    VerifyUserPB makes sure the parameter block passed to IndexedSearch has
  224. **    valid parameters. By making this check once, we don't have to worry about
  225. **    things like NULL pointers, strings being too long, etc.
  226. **    VerifyUserPB also determines if the search includes files and/or
  227. **    directories, and determines if a full or partial name search was requested.
  228. */
  229. static    OSErr    VerifyUserPB(CSParamPtr userPB,
  230.                              Boolean *includeFiles,
  231.                              Boolean *includeDirs,
  232.                              Boolean *includeNames)
  233. {
  234.     CInfoPBPtr    searchInfo1;
  235.     CInfoPBPtr    searchInfo2;
  236.     
  237.     searchInfo1 = userPB->ioSearchInfo1;
  238.     searchInfo2 = userPB->ioSearchInfo2;
  239.     
  240.     /* ioMatchPtr cannot be NULL */
  241.     if ( userPB->ioMatchPtr == NULL )
  242.         goto ParamErrExit;
  243.     
  244.     /* ioSearchInfo1 cannot be NULL */
  245.     if ( searchInfo1 == NULL )
  246.         goto ParamErrExit;
  247.     
  248.     /* If any bits except partialName, fullName, or negate are set, then */
  249.     /* ioSearchInfo2 cannot be NULL because information in ioSearchInfo2 is required  */
  250.     if ( ((userPB->ioSearchBits & ~(fsSBPartialName | fsSBFullName | fsSBNegate)) != 0) &&
  251.          ( searchInfo2 == NULL ))
  252.         goto ParamErrExit;
  253.     
  254.     *includeFiles = false;
  255.     *includeDirs = false;
  256.     *includeNames = false;
  257.     
  258.     if ( (userPB->ioSearchBits & (fsSBPartialName | fsSBFullName)) != 0 )
  259.     {
  260.         /* If any kind of name matching is requested, then ioNamePtr in */
  261.         /* ioSearchInfo1 cannot be NULL or a zero-length string */
  262.         if ( (searchInfo1->hFileInfo.ioNamePtr == NULL) ||
  263.              (searchInfo1->hFileInfo.ioNamePtr[0] == 0) ||
  264.              (searchInfo1->hFileInfo.ioNamePtr[0] > (sizeof(Str63) - 1)) )
  265.             goto ParamErrExit;
  266.         
  267.         *includeNames = true;
  268.     }
  269.     
  270.     if ( (userPB->ioSearchBits & fsSBFlAttrib) != 0 )
  271.     {
  272.         /* The only attributes you can search on are the directory flag */
  273.         /* and the locked flag. */
  274.         if ( (searchInfo2->hFileInfo.ioFlAttrib & ~(ioDirMask | 0x01)) != 0 )
  275.             goto ParamErrExit;
  276.         
  277.         /* interested in the directory bit? */
  278.         if ( (searchInfo2->hFileInfo.ioFlAttrib & ioDirMask) != 0 )
  279.         {
  280.             /* yes, so do they want just directories or just files? */
  281.             if ( (searchInfo1->hFileInfo.ioFlAttrib & ioDirMask) != 0 )
  282.                 *includeDirs = true;
  283.             else
  284.                 *includeFiles = true;
  285.         }
  286.         else
  287.         {
  288.             /* no interest in directory bit - get both files and directories */
  289.             *includeDirs = true;
  290.             *includeFiles = true;
  291.         }
  292.     }
  293.     else
  294.     {
  295.         /* no attribute checking - get both files and directories */
  296.         *includeDirs = true;
  297.         *includeFiles = true;
  298.     }
  299.     
  300.     /* If directories are included in the search, */
  301.     /* then the locked attribute cannot be requested. */
  302.     if ( *includeDirs &&
  303.          ((userPB->ioSearchBits & fsSBFlAttrib) != 0) &&
  304.          ((searchInfo2->hFileInfo.ioFlAttrib & 0x01) != 0) )
  305.         goto ParamErrExit;
  306.     
  307.     /* If files are included in the search, then there cannot be */
  308.     /* a search on the number of files. */
  309.     if ( *includeFiles &&
  310.          ((userPB->ioSearchBits & fsSBDrNmFls) != 0) )
  311.         goto ParamErrExit;
  312.     
  313.     /* If directories are included in the search, then there cannot */
  314.     /* be a search on file lengths. */
  315.     if ( *includeDirs &&
  316.          ((userPB->ioSearchBits & (fsSBFlLgLen | fsSBFlPyLen | fsSBFlRLgLen | fsSBFlRPyLen)) != 0) )
  317.         goto ParamErrExit;
  318.     
  319.     return ( noErr );
  320.          
  321. ParamErrExit:
  322.     return ( paramErr );
  323. }
  324.  
  325. /*****************************************************************************/
  326.  
  327. /*
  328. **    IsSubString checks to see if a string is a substring of another string.
  329. **    Both input strings have already been converted to all uppercase using
  330. **    UprString (the same non-international call the File Manager uses).
  331. */
  332. static    Boolean    IsSubString(StringPtr aStringPtr,
  333.                             StringPtr subStringPtr)
  334. {
  335.     short    strLength;        /* length of string */
  336.     short    subStrLength;    /* length of subString */
  337.     Boolean    found;            /* result of test */
  338.     short    index;            /* current index into string */
  339.     
  340.     found = false;
  341.     strLength = aStringPtr[0];
  342.     subStrLength = subStringPtr[0];
  343.         
  344.     if ( subStrLength <= strLength)
  345.     {
  346.         register short    count;            /* search counter */
  347.         register short    strIndex;        /* running index into string */
  348.         register short    subStrIndex;    /* running index into subString */
  349.         
  350.         /* start looking at first character */
  351.         index = 1;
  352.         
  353.         /* continue looking until remaining string is shorter than substring */
  354.         count = strLength - subStrLength + 1;
  355.         
  356.         do
  357.         {
  358.             strIndex = index;    /* start string index at index */
  359.             subStrIndex = 1;    /* start subString index at 1 */
  360.             
  361.             while ( !found && (aStringPtr[strIndex] == subStringPtr[subStrIndex]) )
  362.             {
  363.                 if ( subStrIndex == subStrLength )
  364.                 {
  365.                     /* all characters in subString were found */
  366.                     found = true;
  367.                 }
  368.                 else
  369.                 {
  370.                     /* check next character of substring against next character of string */
  371.                     ++subStrIndex;
  372.                     ++strIndex;
  373.                 }
  374.             }
  375.             
  376.             if ( !found )
  377.             {
  378.                 /* start substring search again at next string character */
  379.                 ++index;
  380.                 --count;
  381.             }
  382.         } while ( count != 0 && (!found) );
  383.     }
  384.     
  385.     return ( found );
  386. }
  387.  
  388. /*****************************************************************************/
  389.  
  390. /*
  391. **    CompareMasked does a bitwise comparison with mask on 1 or more longs.
  392. **    data1 and data2 are first exclusive-ORed together resulting with bits set
  393. **    where they are different. That value is then ANDed with the mask resulting
  394. **    with bits set if the test fails. true is returned if the tests pass.
  395. */
  396. static    Boolean    CompareMasked(const long *data1,
  397.                               const long *data2,
  398.                               const long *mask,
  399.                               short longsToCompare)
  400. {
  401.     Boolean    result = true;
  402.     
  403.     while ( (longsToCompare != 0) && (result == true) )
  404.     {
  405.         /* (*data1 ^ *data2) = bits that are different, so... */
  406.         /* ((*data1 ^ *data2) & *mask) = bits that are different that we're interested in */
  407.         
  408.         if ( ((*data1 ^ *data2) & *mask) != 0 )
  409.             result = false;
  410.         
  411.         ++data1;
  412.         ++data2;
  413.         ++mask;
  414.         --longsToCompare;
  415.     }
  416.     
  417.     return ( result );
  418. }
  419.  
  420. /*****************************************************************************/
  421.  
  422. /*
  423. **    Check for matches compares the search criteria in userPB to the file
  424. **    system object in cPB. If there's a match, then the information in cPB is
  425. **    is added to the match array and the actual match count is incremented.
  426. */
  427. static    void    CheckForMatches(CInfoPBPtr cPB,
  428.                                 CSParamPtr userPB,
  429.                                 const Str63 matchName,
  430.                                 Boolean includeFiles,
  431.                                 Boolean includeDirs)
  432. {
  433.     long        searchBits;
  434.     CInfoPBPtr    searchInfo1;
  435.     CInfoPBPtr    searchInfo2;
  436.     Str63        itemName;        /* copy of object's name for partial name matching */
  437.     Boolean        foundMatch;
  438.     
  439.     foundMatch = false;            /* default to no match */
  440.     
  441.     searchBits = userPB->ioSearchBits;
  442.     searchInfo1 = userPB->ioSearchInfo1;
  443.     searchInfo2 = userPB->ioSearchInfo2;
  444.     
  445.     /* Into the if statements that go on forever... */
  446.     
  447.     if ( (cPB->hFileInfo.ioFlAttrib & ioDirMask) == 0 )
  448.     {
  449.         if (!includeFiles)
  450.             goto Failed;
  451.     }
  452.     else
  453.     {
  454.         if (!includeDirs)
  455.             goto Failed;
  456.     }
  457.     
  458.     if ( (searchBits & fsSBPartialName) != 0 )
  459.     {
  460.         if ( (cPB->hFileInfo.ioNamePtr[0] > 0) &&
  461.              (cPB->hFileInfo.ioNamePtr[0] <= (sizeof(Str63) - 1)) )
  462.         {
  463.             /* Make uppercase copy of object name */
  464.             BlockMoveData(cPB->hFileInfo.ioNamePtr,
  465.                             itemName,
  466.                             cPB->hFileInfo.ioNamePtr[0] + 1);
  467.             /* Use the same non-international call the File Manager uses */
  468.             UpperString(itemName, true);
  469.         }
  470.         else
  471.         {
  472.             goto Failed;
  473.         }
  474.         
  475.         {
  476.             if ( !IsSubString(itemName, (StringPtr)matchName) )
  477.             {
  478.                 goto Failed;
  479.             }
  480.             else if ( searchBits == fsSBPartialName )
  481.             {
  482.                 /* optimize for name matching only since it is most common way to search */
  483.                 goto Hit;
  484.             }
  485.         }
  486.     }
  487.     
  488.     if ( (searchBits & fsSBFullName) != 0 )
  489.     {
  490.         /* Use the same non-international call the File Manager uses */
  491.         if ( !EqualString(cPB->hFileInfo.ioNamePtr, matchName, false, true) )
  492.         {
  493.             goto Failed;
  494.         }
  495.         else if ( searchBits == fsSBFullName )
  496.         {
  497.             /* optimize for name matching only since it is most common way to search */
  498.             goto Hit;
  499.         }
  500.     }
  501.     
  502.     if ( (searchBits & fsSBFlParID) != 0 )
  503.     {
  504.         if ( ((unsigned long)(cPB->hFileInfo.ioFlParID) < (unsigned long)(searchInfo1->hFileInfo.ioFlParID)) ||
  505.              ((unsigned long)(cPB->hFileInfo.ioFlParID) > (unsigned long)(searchInfo2->hFileInfo.ioFlParID)) )
  506.         {
  507.             goto Failed;
  508.         }
  509.     }
  510.     
  511.     if ( (searchBits & fsSBFlAttrib) != 0 )
  512.     {
  513.         if ( ((cPB->hFileInfo.ioFlAttrib ^ searchInfo1->hFileInfo.ioFlAttrib) &
  514.               searchInfo2->hFileInfo.ioFlAttrib) != 0 )
  515.         {
  516.             goto Failed;
  517.         }
  518.     }
  519.     
  520.     if ( (searchBits & fsSBDrNmFls) != 0 )
  521.     {
  522.         if ( ((unsigned long)(cPB->dirInfo.ioDrNmFls) < (unsigned long)(searchInfo1->dirInfo.ioDrNmFls)) ||
  523.              ((unsigned long)(cPB->dirInfo.ioDrNmFls) > (unsigned long)(searchInfo2->dirInfo.ioDrNmFls)) )
  524.         {
  525.             goto Failed;
  526.         }
  527.     }
  528.  
  529.     if ( (searchBits & fsSBFlFndrInfo) != 0 )    /* fsSBFlFndrInfo is same as fsSBDrUsrWds */
  530.     {
  531.         if ( !CompareMasked((long *)&(cPB->hFileInfo.ioFlFndrInfo),
  532.                             (long *)&(searchInfo1->hFileInfo.ioFlFndrInfo),
  533.                             (long *)&(searchInfo2->hFileInfo.ioFlFndrInfo),
  534.                             sizeof(FInfo) / sizeof(long)) )
  535.         {
  536.             goto Failed;
  537.         }
  538.     }
  539.     
  540.     if ( (searchBits & fsSBFlXFndrInfo) != 0 )    /* fsSBFlXFndrInfo is same as fsSBDrFndrInfo */
  541.     {
  542.         if ( !CompareMasked((long *)&(cPB->hFileInfo.ioFlXFndrInfo),
  543.                             (long *)&(searchInfo1->hFileInfo.ioFlXFndrInfo),
  544.                             (long *)&(searchInfo2->hFileInfo.ioFlXFndrInfo),
  545.                             sizeof(FXInfo) / sizeof(long)) )
  546.         {
  547.             goto Failed;
  548.         }
  549.     }
  550.     
  551.     if ( (searchBits & fsSBFlLgLen) != 0 )
  552.     {
  553.         if ( ((unsigned long)(cPB->hFileInfo.ioFlLgLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlLgLen)) ||
  554.              ((unsigned long)(cPB->hFileInfo.ioFlLgLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlLgLen)) )
  555.         {
  556.             goto Failed;
  557.         }
  558.     }
  559.  
  560.     if ( (searchBits & fsSBFlPyLen) != 0 )
  561.     {
  562.         if ( ((unsigned long)(cPB->hFileInfo.ioFlPyLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlPyLen)) ||
  563.              ((unsigned long)(cPB->hFileInfo.ioFlPyLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlPyLen)) )
  564.         {
  565.             goto Failed;
  566.         }
  567.     }
  568.  
  569.     if ( (searchBits & fsSBFlRLgLen) != 0 )
  570.     {
  571.         if ( ((unsigned long)(cPB->hFileInfo.ioFlRLgLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlRLgLen)) ||
  572.              ((unsigned long)(cPB->hFileInfo.ioFlRLgLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlRLgLen)) )
  573.         {
  574.             goto Failed;
  575.         }
  576.     }
  577.  
  578.     if ( (searchBits & fsSBFlRPyLen) != 0 )
  579.     {
  580.         if ( ((unsigned long)(cPB->hFileInfo.ioFlRPyLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlRPyLen)) ||
  581.              ((unsigned long)(cPB->hFileInfo.ioFlRPyLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlRPyLen)) )
  582.         {
  583.             goto Failed;
  584.         }
  585.     }
  586.  
  587.     if ( (searchBits & fsSBFlCrDat) != 0 )    /* fsSBFlCrDat is same as fsSBDrCrDat */
  588.     {
  589.         if ( ((unsigned long)(cPB->hFileInfo.ioFlCrDat) < (unsigned long)(searchInfo1->hFileInfo.ioFlCrDat)) ||
  590.              ((unsigned long)(cPB->hFileInfo.ioFlCrDat) > (unsigned long)(searchInfo2->hFileInfo.ioFlCrDat)) )
  591.         {
  592.             goto Failed;
  593.         }
  594.     }
  595.  
  596.     if ( (searchBits & fsSBFlMdDat) != 0 )    /* fsSBFlMdDat is same as fsSBDrMdDat */
  597.     {
  598.         if ( ((unsigned long)(cPB->hFileInfo.ioFlMdDat) < (unsigned long)(searchInfo1->hFileInfo.ioFlMdDat)) ||
  599.              ((unsigned long)(cPB->hFileInfo.ioFlMdDat) > (unsigned long)(searchInfo2->hFileInfo.ioFlMdDat)) )
  600.         {
  601.             goto Failed;
  602.         }
  603.     }
  604.  
  605.     if ( (searchBits & fsSBFlBkDat) != 0 )    /* fsSBFlBkDat is same as fsSBDrBkDat */
  606.     {
  607.         if ( ((unsigned long)(cPB->hFileInfo.ioFlBkDat) < (unsigned long)(searchInfo1->hFileInfo.ioFlBkDat)) ||
  608.              ((unsigned long)(cPB->hFileInfo.ioFlBkDat) > (unsigned long)(searchInfo2->hFileInfo.ioFlBkDat)) )
  609.         {
  610.             goto Failed;
  611.         }
  612.     }
  613.  
  614.     /* Hey, we passed all of the tests! */
  615.     
  616. Hit:
  617.     foundMatch = true;
  618.  
  619. /* foundMatch is false if code jumps to Failed */
  620. Failed:
  621.     /* Do we reverse our findings? */
  622.     if ( (searchBits & fsSBNegate) != 0 )
  623.         foundMatch = !foundMatch;    /* matches are not, not matches are */
  624.     
  625.     if ( foundMatch )
  626.     {
  627.  
  628.         /* Move the match into the match buffer */
  629.         userPB->ioMatchPtr[userPB->ioActMatchCount].vRefNum = cPB->hFileInfo.ioVRefNum;
  630.         userPB->ioMatchPtr[userPB->ioActMatchCount].parID = cPB->hFileInfo.ioFlParID;
  631.         if ( cPB->hFileInfo.ioNamePtr[0] > 63 )
  632.             cPB->hFileInfo.ioNamePtr[0] = 63;
  633.         BlockMoveData(cPB->hFileInfo.ioNamePtr,
  634.                       userPB->ioMatchPtr[userPB->ioActMatchCount].name,
  635.                       cPB->hFileInfo.ioNamePtr[0] + 1);
  636.         
  637.         /* increment the actual count */
  638.         ++(userPB->ioActMatchCount);
  639.     }
  640. }
  641.  
  642. /*****************************************************************************/
  643.  
  644. /*
  645. **    TimeOutTask is executed when the timer goes off. It simply sets the
  646. **    stopSearch field to true. After each object is found and possibly added
  647. **    to the matches buffer, stopSearch is checked to see if the search should
  648. **    continue.
  649. */
  650.  
  651. #if GENERATINGCFM
  652.  
  653. static    pascal    void    TimeOutTask(TMTaskPtr tmTaskPtr)
  654. {
  655.     ((ExtendedTMTaskPtr)tmTaskPtr)->stopSearch = true;
  656. }
  657.  
  658. #else
  659.  
  660. static    pascal    TMTaskPtr    GetTMTaskPtr(void)
  661.     ONEWORDINLINE(0x2e89);    /* MOVE.L A1,(SP) */
  662.     
  663. static    void    TimeOutTask(void)
  664. {
  665.     ((ExtendedTMTaskPtr)GetTMTaskPtr())->stopSearch = true;
  666. }
  667.  
  668. #endif
  669.  
  670. /*****************************************************************************/
  671.  
  672. /*
  673. **    GetDirModDate returns the modification date of a directory. If there is
  674. **    an error getting the modification date, -1 is returned to indicate
  675. **    something went wrong.
  676. */
  677. static    long    GetDirModDate(short vRefNum,
  678.                               long dirID)
  679. {
  680.     CInfoPBRec pb;
  681.     Str31 tempName;
  682.     long modDate;
  683.  
  684.     /* Protection against File Sharing problem */
  685.     tempName[0] = 0;
  686.     pb.dirInfo.ioNamePtr = tempName;
  687.     pb.dirInfo.ioVRefNum = vRefNum;
  688.     pb.dirInfo.ioDrDirID = dirID;
  689.     pb.dirInfo.ioFDirIndex = -1;    /* use ioDrDirID */
  690.     if ( PBGetCatInfoSync(&pb) == noErr )
  691.     {
  692.         modDate = pb.dirInfo.ioDrMdDat;
  693.     }
  694.     else
  695.     {
  696.         modDate = -1;
  697.     }
  698.     
  699.     return ( modDate );
  700. }
  701.  
  702. /*****************************************************************************/
  703.  
  704. pascal    OSErr    IndexedSearch(CSParamPtr pb,
  705.                               long dirID)
  706. {
  707.     static LevelRecHandle    searchStack = NULL;        /* static handle to LevelRec stack */
  708.     static Size                searchStackSize = 0;    /* size of static handle */
  709.     SearchPositionRecPtr    catPosition;
  710.     long                    modDate;
  711.     short                    index;
  712.     ExtendedTMTask            timerTask;
  713.     OSErr                    result;
  714.     short                    realVRefNum;
  715.     Str63                    itemName;
  716.     CInfoPBRec                cPB;
  717.     long                    tempLong;
  718.     Boolean                    includeFiles;
  719.     Boolean                    includeDirs;
  720.     Boolean                    includeNames;
  721.     Str63                    upperName;
  722.     
  723.     timerTask.stopSearch = false;    /* don't stop yet! */
  724.     
  725.     /* If request has a timeout, install a Time Manager task. */
  726.     if ( pb->ioSearchTime != 0 )
  727.     {
  728.         /* Start timer */
  729.         timerTask.theTask.tmAddr = NewTimerProc(TimeOutTask);
  730.         InsTime((QElemPtr)&(timerTask.theTask));
  731.         PrimeTime((QElemPtr)&(timerTask.theTask), pb->ioSearchTime);
  732.     }
  733.     
  734.     /* Check the parameter block passed for things that we don't want to assume */
  735.     /* are OK later in the code. For example, make sure pointers to data structures */
  736.     /* and buffers are not NULL.  And while we're in there, see if the request */
  737.     /* specified searching for files, directories, or both, and see if the search */
  738.     /* was by full or partial name. */
  739.     result = VerifyUserPB(pb, &includeFiles, &includeDirs, &includeNames);
  740.     if ( result == noErr )
  741.     {
  742.         pb->ioActMatchCount = 0;    /* no matches yet */
  743.     
  744.         if ( includeNames )
  745.         {
  746.             /* The search includes seach by full or partial name. */
  747.             /* Make an upper case copy of the match string to pass to */
  748.             /* CheckForMatches. */
  749.             BlockMoveData(pb->ioSearchInfo1->hFileInfo.ioNamePtr,
  750.                             upperName,
  751.                             pb->ioSearchInfo1->hFileInfo.ioNamePtr[0] + 1);
  752.             /* Use the same non-international call the File Manager uses */
  753.             UpperString(upperName, true);
  754.         }
  755.         
  756.         /* Prevent casting to my type throughout code */
  757.         catPosition = (SearchPositionRecPtr)&pb->ioCatPosition;
  758.         
  759.         /* Create searchStack first time called */
  760.         if ( searchStack == NULL )
  761.         {
  762.             searchStack = (LevelRecHandle)NewHandle(kAdditionalLevelRecs * sizeof(LevelRec));
  763.         }
  764.         
  765.         /* Make sure searchStack really exists */
  766.         if ( searchStack != NULL )
  767.         {
  768.             searchStackSize = GetHandleSize((Handle)searchStack);
  769.             
  770.             /* See if the search is a new search or a resumed search. */
  771.             if ( catPosition->initialize == 0 )
  772.             {
  773.                 /* New search. */
  774.                 
  775.                 /* Get the real vRefNum and fill in catPosition->initialize. */ 
  776.                 result = CheckVol(pb->ioNamePtr, pb->ioVRefNum, &realVRefNum, &catPosition->initialize);
  777.                 if ( result == noErr )
  778.                 {
  779.                     /* clear searchStack */
  780.                     catPosition->stackDepth = 0;
  781.                     
  782.                     /* use dirID parameter passed and... */
  783.                     index = -1;    /* start with the passed directory itself! */
  784.                 }
  785.             }
  786.             else
  787.             {
  788.                 /* We're resuming a search. */
  789.     
  790.                 /* Get the real vRefNum and make sure catPosition->initialize is valid. */ 
  791.                 result = CheckVol(pb->ioNamePtr, pb->ioVRefNum, &realVRefNum, &tempLong);
  792.                 if ( result == noErr )
  793.                 {
  794.                     /* Make sure the resumed search is to the same volume! */
  795.                     if ( catPosition->initialize == tempLong )
  796.                     {
  797.                         /* For resume, catPosition->stackDepth > 0 */
  798.                         if ( catPosition->stackDepth > 0 )
  799.                         {
  800.                             /* Position catPosition->stackDepth to access last saved level */
  801.                             --(catPosition->stackDepth);
  802.             
  803.                             /* Get the dirID and index for the next item */
  804.                             dirID = (*searchStack)[catPosition->stackDepth].dirID;
  805.                             index = (*searchStack)[catPosition->stackDepth].index;
  806.                             
  807.                             /* Check the dir's mod date against the saved mode date on our "stack" */
  808.                             modDate = GetDirModDate(realVRefNum, dirID);
  809.                             if ( modDate != (*searchStack)[catPosition->stackDepth].dirModDate )
  810.                                 result = catChangedErr;
  811.                         }
  812.                         else
  813.                         {
  814.                             /* Invalid catPosition record was passed */
  815.                             result = paramErr;
  816.                         }
  817.                     }
  818.                     else
  819.                     {
  820.                         /* The volume is not the same */
  821.                         result = catChangedErr;
  822.                     }
  823.                 }
  824.             }
  825.             
  826.             if ( result == noErr )
  827.             {
  828.                 /* ioNamePtr and ioVRefNum only need to be set up once. */
  829.                 cPB.hFileInfo.ioNamePtr = itemName;
  830.                 cPB.hFileInfo.ioVRefNum = realVRefNum;
  831.                 
  832.                 /*
  833.                 **    Here's the loop that:
  834.                 **        Finds the next item on the volume.
  835.                 **        If noErr, calls the code to check for matches and add matches
  836.                 **            to the match buffer.
  837.                 **        Sets up dirID and index for to find the next item on the volume.
  838.                 **
  839.                 **    The looping ends when:
  840.                 **        a) an unexpected error is returned by PBGetCatInfo. All that
  841.                 **            is expected is noErr and fnfErr (after the last item in a
  842.                 **            directory is found).
  843.                 **        b) the caller specified a timeout and our Time Manager task
  844.                 **            has fired.
  845.                 **        c) the number of matches requested by the caller has been found.
  846.                 **        d) the last item on the volume was found.
  847.                 */
  848.                 do
  849.                 {
  850.                     /* get the next item */
  851.                     cPB.hFileInfo.ioFDirIndex = index;
  852.                     cPB.hFileInfo.ioDirID = dirID;
  853.                     result = PBGetCatInfoSync(&cPB);
  854.                     if ( index != -1 )
  855.                     {
  856.                         if ( result == noErr )
  857.                         {
  858.                             /* We found something */
  859.         
  860.                             CheckForMatches(&cPB, pb, upperName, includeFiles, includeDirs);
  861.                             
  862.                             ++index;
  863.                             if ( (cPB.dirInfo.ioFlAttrib & ioDirMask) != 0 )
  864.                             {
  865.                                 /* It's a directory */
  866.                                 
  867.                                 result = CheckStack(catPosition->stackDepth, searchStack, &searchStackSize);
  868.                                 if ( result == noErr )
  869.                                 {
  870.                                     /* Save the current state on the searchStack */
  871.                                     /* when we come back, this is where we'll start */
  872.                                     (*searchStack)[catPosition->stackDepth].dirID = dirID;
  873.                                     (*searchStack)[catPosition->stackDepth].index = index;
  874.                                     (*searchStack)[catPosition->stackDepth].dirModDate = GetDirModDate(realVRefNum, dirID);
  875.                                     
  876.                                     /* position catPosition->stackDepth for next saved level */
  877.                                     ++(catPosition->stackDepth);
  878.                                     
  879.                                     /* The next item to get is the 1st item in the child directory */
  880.                                     dirID = cPB.dirInfo.ioDrDirID;
  881.                                     index = 1;
  882.                                 }
  883.                             }
  884.                             /* else do nothing for files */
  885.                         }
  886.                         else
  887.                         {
  888.                             /* End of directory found (or we had some error and that */
  889.                             /* means we have to drop out of this directory). */
  890.                             /* Restore last thing put on stack and */
  891.                             /* see if we need to continue or quit. */
  892.                             if ( catPosition->stackDepth > 0 )
  893.                             {
  894.                                 /* position catPosition->stackDepth to access last saved level */
  895.                                 --(catPosition->stackDepth);
  896.                                 
  897.                                 dirID = (*searchStack)[catPosition->stackDepth].dirID;
  898.                                 index = (*searchStack)[catPosition->stackDepth].index;
  899.                                 
  900.                                 /* Check the dir's mod date against the saved mode date on our "stack" */
  901.                                 modDate = GetDirModDate(realVRefNum, dirID);
  902.                                 if ( modDate != (*searchStack)[catPosition->stackDepth].dirModDate )
  903.                                 {
  904.                                     result = catChangedErr;
  905.                                 }
  906.                                 else
  907.                                 {
  908.                                     /* Going back to ancestor directory. */
  909.                                     /* Clear error so we can continue. */
  910.                                     result = noErr;
  911.                                 }
  912.                             }
  913.                             else
  914.                             {
  915.                                 /* We hit the bottom of the stack, so we'll let the */
  916.                                 /* the eofErr drop us out of the loop. */
  917.                                 result = eofErr;
  918.                             }
  919.                         }
  920.                     }
  921.                     else
  922.                     {
  923.                         /* Special case for index == -1; that means that we're starting */
  924.                         /* a new search and so the first item to check is the directory */
  925.                         /* passed to us. */
  926.                         if ( result == noErr )
  927.                         {
  928.                             /* We found something */
  929.         
  930.                             CheckForMatches(&cPB, pb, upperName, includeFiles, includeDirs);
  931.                             
  932.                             /* Now, set the index to 1 and then we're ready to look inside */
  933.                             /* the passed directory. */
  934.                             index = 1;
  935.                         }
  936.                     }
  937.                 } while ( (!timerTask.stopSearch) &&    /* timer hasn't fired */
  938.                           (result == noErr) &&            /* no unexpected errors */
  939.                           (pb->ioReqMatchCount > pb->ioActMatchCount) ); /* we haven't found our limit */
  940.                 
  941.                 /* Did we drop out of the loop because of timeout or */
  942.                 /* ioReqMatchCount was found? */
  943.                 if ( result == noErr )
  944.                 {
  945.                     result = CheckStack(catPosition->stackDepth, searchStack, &searchStackSize);
  946.                     if ( result == noErr )
  947.                     {
  948.                         /* Either there was a timeout or ioReqMatchCount was reached. */
  949.                         /* Save the dirID and index for the next time we're called. */
  950.                         
  951.                         (*searchStack)[catPosition->stackDepth].dirID = dirID;
  952.                         (*searchStack)[catPosition->stackDepth].index = index;
  953.                         (*searchStack)[catPosition->stackDepth].dirModDate = GetDirModDate(realVRefNum, dirID);
  954.                         
  955.                         /* position catPosition->stackDepth for next saved level */
  956.                         
  957.                         ++(catPosition->stackDepth);
  958.                     }
  959.                 }
  960.             }
  961.         }
  962.         else
  963.         {
  964.             /* searchStack Handle could not be allocated */
  965.             result = memFullErr;
  966.         }
  967.     }
  968.     
  969.     if ( pb->ioSearchTime != 0 )
  970.     {
  971.         /* Stop Time Manager task here if it was installed */
  972.         RmvTime((QElemPtr)&(timerTask.theTask));
  973.         DisposeRoutineDescriptor(timerTask.theTask.tmAddr);
  974.     }
  975.     
  976.     return ( result );
  977. }
  978.  
  979. /*****************************************************************************/
  980.  
  981. pascal OSErr PBCatSearchSyncCompat(CSParamPtr paramBlock)
  982. {
  983.     static Boolean            fullExtFSDispatchingtested = false;
  984.     static Boolean            hasFullExtFSDispatching = false;
  985.     OSErr                     result;
  986.     Boolean                    supportsCatSearch;
  987.     long                    response;
  988.     GetVolParmsInfoBuffer    volParmsInfo;
  989.     long                    infoSize;
  990.     
  991.     result = noErr;
  992.  
  993.     /* See if File Manager will pass CatSearch requests to external file systems */
  994.     /* we'll store the results in a static variable so we don't have to call Gestalt */
  995.     /* everytime we're called. */
  996.     if ( !fullExtFSDispatchingtested )
  997.     {
  998.         fullExtFSDispatchingtested = true;
  999.         if ( Gestalt(gestaltFSAttr, &response) == noErr )
  1000.         {
  1001.             hasFullExtFSDispatching = ((response & (1L << gestaltFullExtFSDispatching)) != 0);
  1002.         }
  1003.     }
  1004.     
  1005.     /* CatSearch is a per volume attribute, so we have to check each time we're */
  1006.     /* called to see if it is available on the volume specified. */
  1007.     supportsCatSearch = false;
  1008.     if ( hasFullExtFSDispatching )
  1009.     {
  1010.         infoSize = sizeof(GetVolParmsInfoBuffer);
  1011.         result = HGetVolParms(paramBlock->ioNamePtr, paramBlock->ioVRefNum,
  1012.                             &volParmsInfo, &infoSize);
  1013.         if ( result == noErr )
  1014.         {
  1015.             supportsCatSearch = hasCatSearch(volParmsInfo);
  1016.         }
  1017.     }
  1018.     
  1019.     /* noErr or paramErr is OK here. */
  1020.     /* paramErr just means that GetVolParms isn't supported by this volume */
  1021.     if ( (result == noErr) || (result == paramErr) )
  1022.     {
  1023.         if ( supportsCatSearch )
  1024.         {
  1025.             /* Volume supports CatSearch so use it. */
  1026.             /* CatSearch is faster than an indexed search. */
  1027.             result = PBCatSearchSync(paramBlock);
  1028.         }
  1029.         else
  1030.         {
  1031.             /* Volume doesn't support CatSearch so */
  1032.             /* search using IndexedSearch from root directory. */
  1033.             result = IndexedSearch(paramBlock, fsRtDirID);
  1034.         }
  1035.     }
  1036.     
  1037.     return ( result );
  1038. }
  1039.  
  1040. /*****************************************************************************/
  1041.  
  1042. pascal    OSErr    NameFileSearch(StringPtr volName,
  1043.                                short vRefNum,
  1044.                                ConstStr255Param fileName,
  1045.                                FSSpecPtr matches,
  1046.                                long reqMatchCount,
  1047.                                long *actMatchCount,
  1048.                                Boolean newSearch,
  1049.                                Boolean partial)
  1050. {
  1051.     CInfoPBRec        searchInfo1, searchInfo2;
  1052.     HParamBlockRec    pb;
  1053.     OSErr            error;
  1054.     static CatPositionRec catPosition;
  1055.     static short    lastVRefNum = 0;
  1056.     
  1057.     /* get the real volume reference number */
  1058.     error = DetermineVRefNum(volName, vRefNum, &vRefNum);
  1059.     if ( error != noErr )
  1060.         return ( error );
  1061.     
  1062.     pb.csParam.ioNamePtr = NULL;
  1063.     pb.csParam.ioVRefNum = vRefNum;
  1064.     pb.csParam.ioMatchPtr = matches;
  1065.     pb.csParam.ioReqMatchCount = reqMatchCount;
  1066.     pb.csParam.ioSearchBits = ( partial ) ?        /* tell CatSearch what we're looking for: */
  1067.         ( fsSBPartialName + fsSBFlAttrib ) :    /* partial name file matches or */
  1068.         ( fsSBFullName + fsSBFlAttrib );        /* full name file matches */
  1069.     pb.csParam.ioSearchInfo1 = &searchInfo1;
  1070.     pb.csParam.ioSearchInfo2 = &searchInfo2;
  1071.     pb.csParam.ioSearchTime = 0;
  1072.     if ( (newSearch) ||                /* If caller specified new search */
  1073.          (lastVRefNum != vRefNum) )    /* or if last search was to another volume, */
  1074.     {
  1075.         catPosition.initialize = 0;    /* then search from beginning of catalog */
  1076.     }
  1077.     pb.csParam.ioCatPosition = catPosition;
  1078.     pb.csParam.ioOptBuffer = GetTempBuffer(0x00004000, &pb.csParam.ioOptBufSize);
  1079.  
  1080.     /* search for fileName */
  1081.     searchInfo1.hFileInfo.ioNamePtr = (StringPtr)fileName;
  1082.     searchInfo2.hFileInfo.ioNamePtr = NULL;
  1083.     
  1084.     /* only match files (not directories) */
  1085.     searchInfo1.hFileInfo.ioFlAttrib = 0x00;
  1086.     searchInfo2.hFileInfo.ioFlAttrib = ioDirMask;
  1087.  
  1088.     error = PBCatSearchSyncCompat((CSParamPtr)&pb);
  1089.     
  1090.     if ( (error == noErr) ||                            /* If no errors or the end of catalog was */
  1091.          (error == eofErr) )                            /* found, then the call was successful so */
  1092.     {
  1093.         *actMatchCount = pb.csParam.ioActMatchCount;    /* return the match count */
  1094.     }
  1095.     else
  1096.     {
  1097.         *actMatchCount = 0;                            /* else no matches found */
  1098.     }
  1099.     
  1100.     if ( (error == noErr) ||                        /* If no errors */
  1101.          (error == catChangedErr) )                    /* or there was a change in the catalog */
  1102.     {
  1103.         catPosition = pb.csParam.ioCatPosition;
  1104.         lastVRefNum = vRefNum;
  1105.             /* we can probably start the next search where we stopped this time */
  1106.     }
  1107.     else
  1108.     {
  1109.         catPosition.initialize = 0;
  1110.             /* start the next search from beginning of catalog */
  1111.     }
  1112.     
  1113.     if ( pb.csParam.ioOptBuffer != NULL )
  1114.     {
  1115.         DisposePtr(pb.csParam.ioOptBuffer);
  1116.     }
  1117.         
  1118.     return ( error );
  1119. }
  1120.  
  1121. /*****************************************************************************/
  1122.  
  1123. pascal    OSErr    CreatorTypeFileSearch(StringPtr volName,
  1124.                                       short vRefNum,
  1125.                                       OSType creator,
  1126.                                       OSType fileType,
  1127.                                       FSSpecPtr matches,
  1128.                                       long reqMatchCount,
  1129.                                       long *actMatchCount,
  1130.                                       Boolean newSearch)
  1131. {
  1132.     CInfoPBRec        searchInfo1, searchInfo2;
  1133.     HParamBlockRec    pb;
  1134.     OSErr            error;
  1135.     static CatPositionRec catPosition;
  1136.     static short    lastVRefNum = 0;
  1137.     
  1138.     /* get the real volume reference number */
  1139.     error = DetermineVRefNum(volName, vRefNum, &vRefNum);
  1140.     if ( error != noErr )
  1141.         return ( error );
  1142.     
  1143.     pb.csParam.ioNamePtr = NULL;
  1144.     pb.csParam.ioVRefNum = vRefNum;
  1145.     pb.csParam.ioMatchPtr = matches;
  1146.     pb.csParam.ioReqMatchCount = reqMatchCount;
  1147.     pb.csParam.ioSearchBits = fsSBFlAttrib + fsSBFlFndrInfo;    /* Looking for finder info file matches */
  1148.     pb.csParam.ioSearchInfo1 = &searchInfo1;
  1149.     pb.csParam.ioSearchInfo2 = &searchInfo2;
  1150.     pb.csParam.ioSearchTime = 0;
  1151.     if ( (newSearch) ||                /* If caller specified new search */
  1152.          (lastVRefNum != vRefNum) )    /* or if last search was to another volume, */
  1153.     {
  1154.         catPosition.initialize = 0;    /* then search from beginning of catalog */
  1155.     }
  1156.     pb.csParam.ioCatPosition = catPosition;
  1157.     pb.csParam.ioOptBuffer = GetTempBuffer(0x00004000, &pb.csParam.ioOptBufSize);
  1158.  
  1159.     /* no fileName */
  1160.     searchInfo1.hFileInfo.ioNamePtr = NULL;
  1161.     searchInfo2.hFileInfo.ioNamePtr = NULL;
  1162.     
  1163.     /* only match files (not directories) */
  1164.     searchInfo1.hFileInfo.ioFlAttrib = 0x00;
  1165.     searchInfo2.hFileInfo.ioFlAttrib = ioDirMask;
  1166.     
  1167.     /* search for creator; if creator = 0x00000000, ignore creator */
  1168.     searchInfo1.hFileInfo.ioFlFndrInfo.fdCreator = creator;
  1169.     searchInfo2.hFileInfo.ioFlFndrInfo.fdCreator = ( creator == (OSType)0x00000000 ) ?
  1170.         (OSType)0x00000000 :
  1171.         (OSType)0xffffffff;
  1172.     
  1173.     /* search for fileType; if fileType = 0x00000000, ignore fileType */
  1174.     searchInfo1.hFileInfo.ioFlFndrInfo.fdType = fileType;
  1175.     searchInfo2.hFileInfo.ioFlFndrInfo.fdType = ( fileType == (OSType)0x00000000 ) ?
  1176.         (OSType)0x00000000 :
  1177.         (OSType)0xffffffff;
  1178.     
  1179.     /* zero all other FInfo fields */
  1180.     searchInfo1.hFileInfo.ioFlFndrInfo.fdFlags = 0;
  1181.     searchInfo1.hFileInfo.ioFlFndrInfo.fdLocation.v = 0;
  1182.     searchInfo1.hFileInfo.ioFlFndrInfo.fdLocation.h = 0;
  1183.     searchInfo1.hFileInfo.ioFlFndrInfo.fdFldr = 0;
  1184.     
  1185.     searchInfo2.hFileInfo.ioFlFndrInfo.fdFlags = 0;
  1186.     searchInfo2.hFileInfo.ioFlFndrInfo.fdLocation.v = 0;
  1187.     searchInfo2.hFileInfo.ioFlFndrInfo.fdLocation.h = 0;
  1188.     searchInfo2.hFileInfo.ioFlFndrInfo.fdFldr = 0;
  1189.  
  1190.     error = PBCatSearchSyncCompat((CSParamPtr)&pb);
  1191.     
  1192.     if ( (error == noErr) ||                            /* If no errors or the end of catalog was */
  1193.          (error == eofErr) )                            /* found, then the call was successful so */
  1194.     {
  1195.         *actMatchCount = pb.csParam.ioActMatchCount;    /* return the match count */
  1196.     }
  1197.     else
  1198.     {
  1199.         *actMatchCount = 0;                            /* else no matches found */
  1200.     }
  1201.     
  1202.     if ( (error == noErr) ||                        /* If no errors */
  1203.          (error == catChangedErr) )                    /* or there was a change in the catalog */
  1204.     {
  1205.         catPosition = pb.csParam.ioCatPosition;
  1206.         lastVRefNum = vRefNum;
  1207.             /* we can probably start the next search where we stopped this time */
  1208.     }
  1209.     else
  1210.     {
  1211.         catPosition.initialize = 0;
  1212.             /* start the next search from beginning of catalog */
  1213.     }
  1214.     
  1215.     if ( pb.csParam.ioOptBuffer != NULL )
  1216.     {
  1217.         DisposePtr(pb.csParam.ioOptBuffer);
  1218.     }
  1219.         
  1220.     return ( error );
  1221. }
  1222.  
  1223. /*****************************************************************************/
  1224.